package com.yeziji.job.utils;

import com.yeziji.job.business.jobInfo.base.JobInfo;
import com.yeziji.job.business.jobInfo.base.QuartzCronJobTrigger;
import com.yeziji.job.common.base.JobContextInfo;
import com.yeziji.job.common.base.QuartzJobExecutorBase;
import com.yeziji.job.common.exception.JobException;
import com.yeziji.job.common.executor.QuartzDisallowConcurrentExecutor;
import com.yeziji.job.common.executor.QuartzExecutor;
import com.yeziji.job.common.msg.JobErrorMsg;
import com.yeziji.job.config.support.listener.JobDefaultListener;
import com.yeziji.job.constant.JobConstants;
import com.yeziji.job.constant.JobMisfirePolicyEnum;
import com.yeziji.job.constant.JobStatusEnum;
import com.yeziji.utils.ServletUtils;
import com.yeziji.utils.expansion.MessageFormat2;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.EverythingMatcher;
import org.springframework.lang.NonNull;

import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;

/**
 * 任务执行工具类
 *
 * @author hwy
 * @since 2024/05/08 16:36
 **/
@Slf4j
public class JobUtils {
    /**
     * 注册任务进调度器
     *
     * @param scheduler 调度器
     * @param jobInfo   任务信息
     */
    public static void register(@NonNull Scheduler scheduler, JobInfo jobInfo) {
        QuartzCronJobTrigger quartzCronJobTrigger = jobInfoConvertToQuartzCronJobTrigger(jobInfo);
        // check job
        try {
            JobKey jobKey = getJobKey(jobInfo);
            // 先检查是否存在相同的 jobKey 如果存在先删除原来的任务
            delete(scheduler, jobKey);

            // 获取下一个有效触发时间，如果获取成功就注册进 quartz 任务调度器当中
            String cron = jobInfo.getCron();
            CronExpression cronExpression = new CronExpression(cron);
            Date nextValidTimeAfter = cronExpression.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
            if (nextValidTimeAfter != null) {
                scheduler.scheduleJob(quartzCronJobTrigger.getJobDetail(), quartzCronJobTrigger.getTrigger());
                scheduler.getListenerManager().addJobListener(new JobDefaultListener(jobInfo.getName()), EverythingMatcher.allJobs());
            }
        } catch (Exception e) {
            log.error("校验注册任务失败: {}", e.getMessage(), e);
            throw new JobException(JobErrorMsg.JOB_VALID_ERROR);
        }

        // 暂停任务
        if (JobStatusEnum.isPaused(jobInfo.getStatus())) {
            paused(scheduler, quartzCronJobTrigger.jobKey());
        }
    }

    /**
     * 获取任务当前正在执行的任务列表
     *
     * @param scheduler 任务调度器
     * @return {@link List}
     */
    public static List<JobExecutionContext> getCurrentlyExecutingJobs(@NonNull Scheduler scheduler) {
        try {
            return scheduler.getCurrentlyExecutingJobs();
        } catch (SchedulerException e) {
            log.error("获取任务执行列表失败: {}", e.getMessage(), e);
            throw new JobException(JobErrorMsg.JOB_OBTAIN_EXEC_LIST_ERROR);
        }
    }

    /**
     * 暂停任务
     *
     * @param scheduler 任务调度器
     * @param jobKey    任务键值
     */
    public static void paused(@NonNull Scheduler scheduler, @NotNull JobKey jobKey) {
        try {
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            log.error("暂停任务失败: {}", e.getMessage(), e);
            throw new JobException(JobErrorMsg.JOB_PAUSED_ERROR);
        }
    }

    /**
     * 检查任务是否存在
     *
     * @param scheduler 任务调度器
     * @param jobKey    任务键值
     * @return {@link Boolean}
     */
    public static boolean exists(@NonNull Scheduler scheduler, @NotNull JobKey jobKey) {
        try {
            return scheduler.checkExists(jobKey);
        } catch (SchedulerException e) {
            log.error("檢查任务失败: {}", e.getMessage(), e);
            throw new JobException(JobErrorMsg.JOB_CHECK_ERROR);
        }
    }

    /**
     * 刪除任务
     *
     * @param scheduler 任务调度器
     * @param jobKey    任务键值
     */
    public static void delete(@NonNull Scheduler scheduler, @NotNull JobKey jobKey) {
        try {
            // 检查 jobKey 是否存在，如果存在才去删除
            if (exists(scheduler, jobKey)) {
                scheduler.deleteJob(jobKey);
            }
        } catch (SchedulerException e) {
            log.error("刪除任务失败: {}", e.getMessage(), e);
            throw new JobException(JobErrorMsg.JOB_DELETE_ERROR);
        }
    }

    /**
     * 根據 jobInfo 獲取 quartz 執行器
     *
     * @param jobInfo 任務信息
     * @return {@link Class} 执行器类型，继承 {@link QuartzJobExecutorBase quartz 任务执行基础类}
     */
    public static Class<? extends Job> getQuartzJobClass(JobInfo jobInfo) {
        Boolean isConcurrent = jobInfo.getIsConcurrent();
        if (isConcurrent == null) {
            return QuartzDisallowConcurrentExecutor.class;
        }
        return isConcurrent ? QuartzExecutor.class : QuartzDisallowConcurrentExecutor.class;
    }

    /**
     * 根据 jobInfo 补充对应的定时任务策略
     *
     * @param jobInfo 任务信息，不同的任务策略不同
     * @param csb     定时任务调度构造器
     * @return {@link CronScheduleBuilder}
     */
    public static CronScheduleBuilder switchCronScheduleMisfirePolicy(JobInfo jobInfo, CronScheduleBuilder csb) {
        Integer misfirePolicy = jobInfo.getMisfirePolicy();
        JobMisfirePolicyEnum jobMisfirePolicyEnum = JobMisfirePolicyEnum.getByCode(misfirePolicy);
        switch (jobMisfirePolicyEnum) {
            case DEFAULT:
                return csb;
            case IGNORE_MISFIRES:
                return csb.withMisfireHandlingInstructionIgnoreMisfires();
            case FIRE_AND_PROCEED:
                return csb.withMisfireHandlingInstructionFireAndProceed();
            case DO_NOTHING:
                return csb.withMisfireHandlingInstructionDoNothing();
            default:
                throw new JobException(JobErrorMsg.JOB_NOT_SUPPORT_MISFIRE_POLICY);
        }
    }

    /**
     * 构建触发器 key 值
     *
     * @param jobInfo 任务信息
     * @return {@link TriggerKey} 触发器键值
     */
    public static TriggerKey getTriggerKey(JobInfo jobInfo) {
        return TriggerKey.triggerKey(JobConstants.JOB_CLASS_NAME + ":" + jobInfo.getName(), jobInfo.getGroupName());
    }

    /**
     * 构造任务 key 值
     *
     * @param jobInfo 任务信息
     * @return {@link JobKey} 任务 key
     */
    public static JobKey getJobKey(JobInfo jobInfo) {
        return JobKey.jobKey(JobConstants.JOB_CLASS_NAME + ":" + jobInfo.getName(), jobInfo.getGroupName());
    }

    /**
     * 将 JobInfo 转化为 QuartzCronJobTrigger
     *
     * @param jobInfo 任务信息
     * @return {@link QuartzCronJobTrigger}
     */
    public static QuartzCronJobTrigger jobInfoConvertToQuartzCronJobTrigger(JobInfo jobInfo) {
        JobKey jobKey = getJobKey(jobInfo);
        TriggerKey triggerKey = getTriggerKey(jobInfo);
        // register job (cron schedule)
        CronScheduleBuilder cronScheduleBuilder = switchCronScheduleMisfirePolicy(jobInfo, CronScheduleBuilder.cronSchedule(jobInfo.getCron()));
        JobDetail jobDetail = JobBuilder.newJob(getQuartzJobClass(jobInfo))
                .withIdentity(jobKey)
                .build();

        // 按新的 cronExpression 表达式构建一个新的 trigger
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .withSchedule(cronScheduleBuilder)
                .withDescription(jobInfo.getRemark())
                .build();

        // 放入参数，运行时的方法可以获取
        jobDetail.getJobDataMap()
                .put(JobConstants.JOB_PARAMS,
                        JobContextInfo.builder()
                                .triggerIp(ServletUtils.getLocalIpAddress())
                                // defaultFlag = JOB_FLAG_GroupName:JobName
                                .flag(MessageFormat2.format(JobConstants.JOB_DEFAULT_FLAG, jobInfo.getGroupName(), jobInfo.getName()))
                                .jobInfo(jobInfo)
                                .build()
                );
        return QuartzCronJobTrigger.builder()
                .jobDetail(jobDetail)
                .trigger(trigger)
                .build();
    }
}
